﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.IO;
using NetFwTypeLib;
using System.Net;
using System.Timers;

namespace BruteForceBlocker
{
    class Program
    {
        
        public class EventDetails
        {
            public DateTime dateAdded { get; set; }
            public string source { get; set; }
            public string data { get; set; }
            public string ip { get; set; }
        }

        static int hoursScan = 1;
        static int numberThreshhold = 10;

        static void Main(string[] args)
        {
            try
            {
                // This part is optional, so that you can alter these variables within the settings
                //hoursScan = Properties.Settings.Default.hoursScanCount;
                //numberThreshhold = Properties.Settings.Default.blockThreshhold;
            }
            catch { }

            timer_Elapsed(null, null);
            Timer timer = new Timer();
            timer.Interval =  3* 60 * 60*1000; // milliseconds
            //timer.Interval = 30 * 1000; // milliseconds
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.AutoReset = true; // if not set you will need to call start after every event fired in the elapsed callback
            timer.Start();

            for (int icount = 0; icount < 100; icount++)
            {
                Console.Read();
                icount = 5;
            }
        
        }

        private static void RunMainCodeBlock(int hoursScan, int numberThreshhold)
        {
            //ScanEventAndBlock(hoursScan, numberThreshhold, "Security", 5152, "The Windows Filtering Platform has blocked a packet", "HAK");
            try
            {
                ScanEventAndBlock(hoursScan, numberThreshhold, "Security", 4625, "An account failed", "RDP");

                ScanEventAndBlock(hoursScan, numberThreshhold, "Application", 18456, "Login failed", "SQL");
            }
            catch (Exception ex)
            {
                Console.WriteLine("*************** Please run this as Administrator ***************");
                Console.WriteLine(ex.Message);
                Console.WriteLine("*************** Please run this as Administrator ***************");
            }
            DateTime now = DateTime.Now;
            Console.WriteLine(now);
        }

        static void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("**** Special Offer : Intel Core i7 920, 2x 750 GB SATA, 12 GB DDR3, USD 69 / month - No Setup Fees");
            Console.WriteLine("**** Windows Server 2012 R2 Standard Edition 25$ / month");
            Console.WriteLine("Running a scan - please wait! - script provided by CL.COM.PK");

            RunMainCodeBlock(hoursScan, numberThreshhold);

            Console.WriteLine("**** Special Offer : Intel Core i7 920, 2x 750 GB SATA, 12 GB DDR3, USD 69 / month - No Setup Fees");
            Console.WriteLine("**** Windows Server 2012 R2 Standard Edition 25$ / month");
            Console.WriteLine("Sleeping for 3 Hours - Script Provided by CL.COM.PK");

        }

        public static bool IsLocalhost(string hostNameOrAddress)
        {
            if (string.IsNullOrEmpty(hostNameOrAddress))
                return false;

            try
            {
                // get host IP addresses
                IPAddress[] hostIPs = Dns.GetHostAddresses(hostNameOrAddress);
                // get local IP addresses
                IPAddress[] localIPs = Dns.GetHostAddresses(Dns.GetHostName());
                // test if any host IP is a loopback IP or is equal to any local IP
                return hostIPs.Any(hostIP => IPAddress.IsLoopback(hostIP) || localIPs.Contains(hostIP));
            }
            catch
            {
                return false;
            }
        }
        private static void ScanEventAndBlock(int hoursScan, int numberThreshhold
            , string logType,int eventId,string failedMessageinEvent
            ,string blockType)
        {
            List<string> alreadyBlocked = new List<string>();
            if (File.Exists( blockType + "_blocked.txt"))
            {
                alreadyBlocked = File.ReadAllLines( blockType + "_blocked.txt").ToList();
            }

            // This stores ip addresses that have been blocked this session
            List<string> newBlocked = new List<string>();

            // Do the scan from the event log, filter by security type
            EventLog ev = new EventLog(logType, System.Environment.MachineName);
            int LastLogToShow = ev.Entries.Count;
            if (LastLogToShow <= 0)
                Console.WriteLine("No Event Logs in the Log :" + logType);

            // Stores this sessions filtered results, should only contain RDP failed attempts
            List<EventDetails> evLists = new List<EventDetails>();

            // Scan through all Security logs extracting the RDP brute force fails
            for (int i = 0; i < ev.Entries.Count; i++)
            {
                EventLogEntry CurrentEntry = ev.Entries[i];
                // 4625 is the only event ID that we are interested in 
                if (CurrentEntry.EventID == eventId)
                {
                    // This filters the event log so that we are only interested in failed attempts within an hour
                    if (CurrentEntry.TimeGenerated >= DateTime.Now.AddHours(-1 * hoursScan))
                    {
                        // This should always be present in the message.
                        if (CurrentEntry.Message.Contains(failedMessageinEvent))
                        {
                            // Regex to extrace the IP address from the message - this is not fool proof, but it's good enough for me.
                            // For example this will probably match in the message if the hacker logs on with a valid IP address e.g. 1.2.3.4 as a username.
                            Regex rg = new Regex(@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b");
                            var matches = rg.Matches(CurrentEntry.Message);
                            if (matches.Count >= 1)
                            {
                                for (int icount = 0; icount < matches.Count; icount++)
                                {
                                    string ipToAdd = matches[icount].Value;
                                    if (!IsLocalhost(ipToAdd) )
                                        evLists.Add(new EventDetails() { data = CurrentEntry.Message, dateAdded = CurrentEntry.TimeGenerated, ip = ipToAdd, source = CurrentEntry.Source });
                                }

                            }
                        }
                    }
                }
            }
            // Groups by IP address
            var numberGroups =
                from n in evLists
                group n.ip by n.ip into g
                select new { Ip = g.Key, Grouper = g };

            // We will output this log string to a file at the end.
            List<string> logs = new List<string>();

            int countBlocked = 0;
            foreach (var evs in numberGroups)
            {
                Console.WriteLine("[" + blockType + "]IP: " + evs.Ip + " count : " + evs.Grouper.Count());

                if (evs.Grouper.Count() >= numberThreshhold)
                {
                    // Excluded IP addresses - for safety these would ideally be your home IP address
                    if (IsLocalhost(evs.Ip))
                    {
                        Console.WriteLine("Ip address excluded from block : " + evs.Ip + " count " + evs.Grouper.Count());
                        logs.Add("Ip address excluded from block : " + evs.Ip + " count " + evs.Grouper.Count());
                    }
                    else
                    {
                        if (alreadyBlocked.Contains(evs.Ip))
                        {
                            Console.WriteLine("[" + blockType + "]IP: " + evs.Ip + " count : " + evs.Grouper.Count() + " already blocked list");
                            logs.Add("IP: " + evs.Ip + " count : " + evs.Grouper.Count() + " already blocked list");
                        }
                        else
                        {
                            // here is where we do the firewall addition blocking
                            countBlocked++;
                            addRule(evs.Ip, evs.Grouper.Count(),blockType);
                            newBlocked.Add(evs.Ip);
                            logs.Add(" blocking IP (" + blockType + "): " + evs.Ip + " count : " + evs.Grouper.Count());
                        }
                    }
                }
            }

            //Logging part - optional
            string fileNAme = DateTime.Now.ToString("dd-MM-yyyy") + "-logs.txt";
            // Change this to whatever you want
            string filePath = blockType + "_" + fileNAme;
            File.AppendAllLines(filePath, logs);
            // Change this to whatever you want
            File.AppendAllLines( blockType + "_blocked.txt", newBlocked);
            Console.WriteLine("[" + blockType + "]*****************************************************");
            Console.WriteLine("[" + blockType + "]done, newly blocked: " + countBlocked);
            Console.WriteLine("[" + blockType + "]*****************************************************");
            ev.Close();
        }

        public static void addRule(string ip, int count,string blockType)
        {
            INetFwRule2 firewallRule = (INetFwRule2)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FWRule"));

            firewallRule.Name = "BruteBlock: (" + blockType + ") IP Access Block " + ip;
            firewallRule.Description = "Block Incoming Connections from IP Address.";
            firewallRule.Action = NET_FW_ACTION_.NET_FW_ACTION_BLOCK;
            firewallRule.Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN;
            firewallRule.Enabled = true;
            firewallRule.InterfaceTypes = "All";
            firewallRule.RemoteAddresses = ip;

            INetFwPolicy2 firewallPolicy = (INetFwPolicy2)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
            firewallPolicy.Rules.Add(firewallRule);

            String msg = "IP Address \"" + ip + "\" Blocked Successfully!";
            Console.WriteLine(msg);
        }
    }
}
